#include "thread_utils.hpp"
#include "lf_queue.hpp"

struct MyStruct {
  int d_[3];  // 简单的结构体载体，用三个整型模拟一份消息。
};

using namespace Common;  // 便于直接访问 LFQueue、createAndStartThread。

/**
 * @brief 消费者线程入口：从 LFQueue 读取元素并打印。
 *
 * LFQueue<T>::getNextToRead() 在队列为空时返回 nullptr，
 * 当前示例依赖单消费者语义且立即检查 size()，理论上不会拿到空指针；
 * 若改为多消费者或更复杂逻辑，应显式判空保证安全。
 */
auto consumeFunction(LFQueue<MyStruct>* lfq) {
  using namespace std::literals::chrono_literals;

  // 先等待 5 秒，给生产者线程时间填充队列，避免立即竞争。
  std::this_thread::sleep_for(5s);

  // size() 通过原子加载 num_elements_，在单消费者场景下可作为读取条件。
  while (lfq->size()) {
    // 由于 LFQueue 仅提供单消费者保证，直接获取当前读指针并读取。
    const auto d = lfq->getNextToRead();  // 若未来扩展为多消费者，此处需检查 nullptr。
    lfq->updateReadIndex();               // 推进读索引并调用 num_elements_--（原子操作）。

    std::cout << "consumeFunction read elem:" << d->d_[0] << "," << d->d_[1] << ","
              << d->d_[2] << " lfq-size:" << lfq->size() << std::endl;

    // 人工放慢消费速度，帮助观察队列长度变化。
    std::this_thread::sleep_for(1s);
  }

  std::cout << "consumeFunction exiting." << std::endl;
}

int main(int, char**) {
  // 初始化容量为 20 的环形队列；容量固定，会在构造时填充默认值。
  LFQueue<MyStruct> lfq(20);

  /**
   * createAndStartThread 返回 heap 分配的 std::thread*，
   * 内部会尝试设置 CPU 亲和性（core_id < 0 时什么也不做）。
   * ⚠️ 函数内部通过引用捕获 lambda，如果 func/args 是临时对象会悬垂，
   *    本例传入的是函数名和指针，属于安全用法。
   */
  auto ct = createAndStartThread(-1, "", consumeFunction, &lfq);

  for (auto i = 0; i < 50; ++i) {
    // 构造一条待写入的数据。
    const MyStruct d{i, i * 10, i * 100};

    /**
     * getNextToWriteTo() 直接返回当前写指针槽位的地址，不进行界限检查；
     * 调用者必须确保不会覆盖仍未被消费者读取的元素。
     * updateWriteIndex() 会执行取模推进，并通过 num_elements_++ 更新元素计数。
     */
    *(lfq.getNextToWriteTo()) = d;
    lfq.updateWriteIndex();

    std::cout << "main constructed elem:" << d.d_[0] << "," << d.d_[1] << ","
              << d.d_[2] << " lfq-size:" << lfq.size() << std::endl;

    using namespace std::literals::chrono_literals;
    std::this_thread::sleep_for(1s);  // 控制生产速度，让消费者有机会读取。
  }

  // 等待消费者线程结束。⚠️ createAndStartThread 返回裸指针，join 后仍需 delete。
  ct->join();
  delete ct;

  std::cout << "main exiting." << std::endl;
  return 0;
}